﻿// ---------------------------------------------------------------------------------------------------------------------
//   Copyright (c) SRT Solutions 2009. All rights reserved.
// ---------------------------------------------------------------------------------------------------------------------

using System;

using NUnit.Framework;

using SRTSolutions.Elevate.FuncExtensions;

namespace SRTSolutions.Elevate.Guide
{
    public class Memoization
    {
        [Test]
        public void HowToMemoize()
        {
            //given a function that takes a long time to calculate a value
            var hitCount = 0;
            Func<int, string> function =
                value =>
                {
                    //***do something complicated and slow here***
                    hitCount++;
                    return value.ToString();
                };

            //normally we'd have to evaluate the function multiple times if we wanted to get a value out
            //(obviously we would just reuse the value if we knew we already called the function,
            // but let's assume these two calls happened in separate functions.
            var result1 = function(2);
            var result2 = function(2);
            Assert.AreEqual(2, hitCount);

            //In this situation, it would be nice if we could cache the results of applying
            //the function inside a lookup table, that way, we'd only ever have to call the
            //function once for any value. Let's do that with one line of code.
            var memoized = function.Memoize();

            //now when we call the function a few times with the same value
            var result3 = memoized(2);
            
            //the function is only evaluated once
            Assert.AreEqual(3, hitCount);
            var result4 = memoized(2);
            var result5 = memoized(2);
            Assert.AreEqual(3, hitCount);

            //and we still get the same result each time
            Assert.AreEqual(result1, result3);
            Assert.AreEqual(result4, result4);
            Assert.AreEqual(result4, result5);

            //NOTE: Memoization is only an effective technique for functions that do not use any
            //      state (AKA have no side effects)
        }

        #region Unit Tests

        [Test]
        public void MemoizeThisException()
        {
            Func<int, int> tmp = null;
            Assert.Throws<ArgumentNullException>(() => tmp.Memoize());
        }

        #endregion

    }
}
